/*
 * Copyright 2003 Program of Computer Graphics, Cornell University
 *     580 Rhodes Hall
 *     Cornell University
 *     Ithaca NY 14853
 * Web: http://www.graphics.cornell.edu/
 * 
 * Not for commercial use. Do not redistribute without permission.
 */
#include "stdafx.h"

#include "SIMDSSE.h"
#include "PoolAllocator.h"
#include "BriteDefs.h"
#include <cstdlib>

class PoolAllocationSegment {
public:
  PoolAllocationSegment *next;
  LONG_PTR freespace;
  char *ptr;

  void *allocateBytes(int size) {
    void *retval = ptr;
    ptr += size;
    freespace -= size;
    assert(freespace>=0);
    return retval;
  }
  void alignPtr(int alignment) { //increment ptr to be enforce specified alignment
    assert((alignment&(alignment-1))==0); //must be power of two
    LONG_PTR offset = -(LONG_PTR)ptr&(alignment-1);
    ptr += offset;
    freespace -= offset;
  }
};


PoolAllocator::PoolAllocator(int _defaultSegmentSize, int _alignment) 
{
  assert((_alignment&(_alignment-1))==0); //must be power of two
  assert(_defaultSegmentSize>=_alignment);
  defaultAlignment = _alignment;
  segmentSize = _defaultSegmentSize;
  numSegments = 0;
  curSegment = NULL;
  totalBytes = 0;
}

PoolAllocator::~PoolAllocator()
{
  deallocateAll();
}

void PoolAllocator::mallocNewSegment(int size) {
  PoolAllocationSegment *seg = (PoolAllocationSegment*)malloc(size);
  assert(seg);
  totalBytes += size;
  numSegments++;
  seg->freespace = size - sizeof(PoolAllocationSegment);
  seg->ptr = ((char*)seg) + sizeof(PoolAllocationSegment);
  seg->next = curSegment;
  curSegment = seg;
}

void PoolAllocator::deallocateAll() {
  PoolAllocationSegment *cur = curSegment;
  while(cur!=NULL) {
    PoolAllocationSegment *next = cur->next;
    free(cur);
    cur = next;
  }
  curSegment = NULL;
  numSegments = 0;
  totalBytes = 0;
}

void *PoolAllocator::allocate(int size, int alignment) {
  assert((alignment&(alignment-1))==0); //must be power of two
  //if not enough room in current segment then allocate a new one
  bool needNewSeg;
  if (curSegment==NULL) {
    needNewSeg = true;
  } else {
    curSegment->alignPtr(alignment);
    needNewSeg = (curSegment->freespace<size);
  }
  if (needNewSeg) {
    mallocNewSegment(maxv(segmentSize,size+sizeof(PoolAllocationSegment)+alignment));
    curSegment->alignPtr(alignment);
  }
  if (curSegment->freespace < size) {
    BRITE_DEBUGBREAK;
  }
  return curSegment->allocateBytes(size);
}


